﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Comp376A2_9609695
{
    /// <summary>
    /// A controller object for the game. Keeps track of objects and the game state.
    /// </summary>
    public class GameController
    {
        private List<Updateable> gameObjects;
        private List<Drawable> drawableObjects;
        private List<Rock> rocks;
        private List<Level> levels;
        private Score scoreObj;
        public DigDug player;
        public int lives;
        private int monsterCount = 0;
        public int monsters
        {
            get { return monsterCount; }
        }
        public int score
        {
            get { return scoreObj.value; }
            set { scoreObj.value = value; }
        }

        public enum GameState { Menu, LevelStart, Game, Paused, LoseLife, LevelDone, GameOver };
        public GameState state;
        public Level level;
        public SpriteFont font;
        private int levelNum = 0;
        public int currLevel
        {
            get { return levelNum + 1; }
        }

        public GameController()
        {
            gameObjects = new List<Updateable>();
            drawableObjects = new List<Drawable>();
            levels = new List<Level>();
            rocks = new List<Rock>();
            state = GameState.Menu;
            lives = 1;
        }

        /// <summary>
        /// Set the Score object to be used
        /// </summary>
        /// <param name="scoreObj">The Score object</param>
        public void setScoreObject(Score scoreObj)
        {
            this.scoreObj = scoreObj;
        }

        /// <summary>
        /// Draw everything that can be drawn
        /// </summary>
        /// <param name="renderer">The SpriteBatch to use</param>
        public void drawAll(SpriteBatch renderer)
        {
            // Draw the level first and the player last
            level.draw(renderer);
            foreach(Drawable obj in drawableObjects)
            {
                if (!(obj is Level))
                    obj.draw(renderer);
            }
            player.draw(renderer);
        }

        /// <summary>
        /// Update everything that can be updated.
        /// </summary>
        /// <param name="gameTime">The game time</param>
        public void updateAll(GameTime gameTime)
        {
            int numObjs = gameObjects.Count;
            for (int i=0; i<numObjs; i++)
            {
                Updateable obj = gameObjects.ElementAt(i);
                obj.update(gameTime, this);
                // When a monster is killed, remove it from the list and add the appropriate number of points
                if (obj is Monster && ((Monster)obj).isDead)
                {
                    gameObjects.Remove(obj);
                    if (obj is Drawable)
                        drawableObjects.Remove((Drawable)obj);
                    monsterCount--;
                    i--;
                    numObjs--;

                    if (obj is Pooka || obj is Fygar)
                    {

                        int value = 200;
                        Monster m = (Monster)obj;
                        if (!m.isSquished)
                        {
                            if (m.position.Y >= level.Y + 64)
                                value = 300;
                            if (m.position.Y >= level.Y + 112)
                                value = 400;
                            if (m.position.Y >= level.Y + 160)
                                value = 500;
                            // Fygars are worth double if they are kill from the side
                            if (m is Fygar && (player.Facing == DigDug.Direction.Left || player.Facing == DigDug.Direction.Right))
                                value *= 2;
                        }
                        else
                            value = 1000; // Squishing is worth 1000 points
                        addObject(new ScorePopup(m.position, value, font));
                        scoreObj.value += value;
                    }
                    // End the level when the monsters are gone
                    if (monsterCount == 0)
                        state = GameState.LevelDone;
                }
                // Remove the score popup if it is finished
                if (obj is ScorePopup && ((ScorePopup)obj).isDead)
                {
                    gameObjects.Remove(obj);
                    drawableObjects.Remove((Drawable)obj);
                    i--;
                    numObjs--;
                }
            }
        }

        /// <summary>
        /// Add an object to be managed by the controller
        /// </summary>
        /// <param name="obj">The object</param>
        public void addObject(Object obj)
        {
            // Check the object's type and add it to the appropriate list
            if (obj is Updateable)
                gameObjects.Add((Updateable) obj);
            if (obj is Drawable)
                drawableObjects.Add((Drawable)obj);
            if (obj is Level)
            {
                levels.Add((Level)obj);
                if (levels.Count == 1)
                    level = (Level)obj;
            }
            if (obj is Monster)
                monsterCount++; // Keep track of the number of monsters
            if (obj is Rock)
                rocks.Add((Rock)obj);
        }

        /// <summary>
        /// Next level!
        /// </summary>
        public void nextLevel()
        {
            levelNum++;
        }

        /// <summary>
        /// Reset all of the monsters.
        /// </summary>
        public void resetMonsters()
        {
            int mCount = gameObjects.Count;
            for (int i = 0; i != mCount; i++)
            {
                Updateable obj = gameObjects.ElementAt(i);
                if (obj is Monster)
                    ((Monster)obj).reset();
            }
            // Also stops any falling or fallen rocks
            foreach (Rock r in rocks)
            {
                if (r.state != Rock.State.Static && r.state != Rock.State.Shaking)
                    r.state = Rock.State.Clear;
            }
        }

        /// <summary>
        /// Check if any rock is colliding
        /// </summary>
        /// <param name="box">The bounding box to check</param>
        /// <returns>True if there is a collision, false otherwise</returns>
        public bool collidesRock(Rectangle box)
        {
            foreach (Rock r in rocks)
            {
                if (r.collides(box))
                    return true;
            }
            return false;
        }

        /// <summary>
        /// Check if any rock is colliding and return the state of that rock
        /// </summary>
        /// <param name="box">The bounding box to check</param>
        /// <param name="state">A reference to a State object to use</param>
        /// <returns>True if there is a collision, false otherwise</returns>
        public bool collidesRock(Rectangle box, ref Rock.State state)
        {
            foreach (Rock r in rocks)
            {
                if (r.collides(box))
                {
                    state = r.state;
                    return true;
                }
            }
            return false;
        }
    }
}
